package com.example.gaoyang.aop;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.example.gaoyang.annotation.NoAuth;
import com.example.gaoyang.domain.SysUser;
import com.example.gaoyang.exception.UnauthorizedException;
import com.example.gaoyang.service.SysUserService;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.Objects;

@Aspect
@Component
@Slf4j
public class LogAspect {

    @Resource
    private SysUserService sysUserService;

    @Around("execution(* com.example.gaoyang.controller.*.*(..))")
    public Object authenticate(ProceedingJoinPoint joinPoint) throws Throwable {
        // 获取当前方法签名
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        // 检查方法是否有 @NoAuth 注解
        if (method.isAnnotationPresent(NoAuth.class)) {
            // 如果有 @NoAuth 注解，则无需鉴权
            return joinPoint.proceed();
        }
        // 获取请求头中的 Token
        HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
        String authorization = request.getHeader(HttpHeaders.AUTHORIZATION);
        // 根据令牌查询用户名
        SysUser sysUser = sysUserService.getOne(new LambdaQueryWrapper<SysUser>()
                .eq(SysUser::getToken, authorization)
        );
        if (authorization == null || sysUser == null || !this.validateToken(sysUser, authorization)) {
            log.error("无效或过期的Token:{},请重新登录!!!", authorization);
            throw new UnauthorizedException("无效或过期的Token,请重新登录!!!");
        }
        // 验证Token后，更新数据库中的token有效时间
        this.updateTokenTime(sysUser);
        return joinPoint.proceed();
    }

    private boolean validateToken(SysUser sysUser, String authorization) {
        // 当前用户名
        String username = sysUser.getUsername();
        // 根据用户名查询数据库中的token和token有效时间
        SysUser user = sysUserService.getOne(new LambdaQueryWrapper<SysUser>()
                .eq(SysUser::getUsername, username)
        );
        // token校验
        String token = user.getToken();
        if (!StringUtils.equals(token, authorization)) {
            return false;
        }
        Date tokenTime = user.getTokenTime();
        // 当前时间大于token时间则失效
        return tokenTime != null && new Date().getTime() <= tokenTime.getTime();
    }

    private void updateTokenTime(SysUser sysUser) {
        SysUser sysUserUpdate = sysUserService.getOne(new LambdaQueryWrapper<SysUser>()
                .eq(SysUser::getUsername, sysUser.getUsername())
        );
        // 失效时间位当前时间+1天
        sysUserUpdate.setTokenTime(new Date(new Date().getTime() + 60 * 60 * 24 * 1000));
        sysUserService.updateById(sysUserUpdate);
    }
}
